<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  <title>SSLSocket入门 | Hexo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description" content="SSL/TLS 协议　　SSL/TLS 协议（RFC2246 RFC4346）处于 TCP/IP 协议与各种应用层协议之间，为数据通讯提供安全支持。从协议内部的功能层面上来看，SSL/TLS 协议可分为两层：　　1. SSL/TLS 记录协议（SSL/TLS Record Protocol），它建立在可靠的传输层协议（如TCP）之上，为上层协议提供数据封装、压缩、加密等基本功能。　　2. SSL/">
<meta property="og:type" content="article">
<meta property="og:title" content="SSLSocket入门">
<meta property="og:url" content="http://yoursite.com/2015/12/23/nio/2.SSL Socket/index.html">
<meta property="og:site_name" content="Hexo">
<meta property="og:description" content="SSL/TLS 协议　　SSL/TLS 协议（RFC2246 RFC4346）处于 TCP/IP 协议与各种应用层协议之间，为数据通讯提供安全支持。从协议内部的功能层面上来看，SSL/TLS 协议可分为两层：　　1. SSL/TLS 记录协议（SSL/TLS Record Protocol），它建立在可靠的传输层协议（如TCP）之上，为上层协议提供数据封装、压缩、加密等基本功能。　　2. SSL/">
<meta property="og:image" content="http://yoursite.com/images/ssl-tls.png">
<meta property="og:updated_time" content="2016-01-29T17:47:36.000Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="SSLSocket入门">
<meta name="twitter:description" content="SSL/TLS 协议　　SSL/TLS 协议（RFC2246 RFC4346）处于 TCP/IP 协议与各种应用层协议之间，为数据通讯提供安全支持。从协议内部的功能层面上来看，SSL/TLS 协议可分为两层：　　1. SSL/TLS 记录协议（SSL/TLS Record Protocol），它建立在可靠的传输层协议（如TCP）之上，为上层协议提供数据封装、压缩、加密等基本功能。　　2. SSL/">
  
    <link rel="alternative" href="/atom.xml" title="Hexo" type="application/atom+xml">
  
  
    <link rel="icon" href="/favicon.png">
  
  <link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
  <link rel="stylesheet" href="/css/style.css" type="text/css">
  

</head>
<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/" id="logo">Hexo</a>
      </h1>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"></a>
        
          <a class="main-nav-link" href="/">Home</a>
        
          <a class="main-nav-link" href="/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
          <a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
        
        <a id="nav-search-btn" class="nav-icon" title="Search"></a>
      </nav>
      <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" results="0" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="http://yoursite.com"></form>
      </div>
    </div>
  </div>
</header>
      <div class="outer">
        <section id="main"><article id="post-nio/2.SSL Socket" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2015/12/23/nio/2.SSL Socket/" class="article-date">
  <time datetime="2015-12-22T18:00:00.000Z" itemprop="datePublished">2015-12-23</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/nio/">nio</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 class="article-title" itemprop="name">
      SSLSocket入门
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="SSL/TLS__u534F_u8BAE"><a href="#SSL/TLS__u534F_u8BAE" class="headerlink" title="SSL/TLS 协议"></a>SSL/TLS 协议</h2><p>　　SSL/TLS 协议（RFC2246 RFC4346）处于 TCP/IP 协议与各种应用层协议之间，为数据通讯提供安全支持。从协议内部的功能层面上来看，SSL/TLS 协议可分为两层：<br>　　1. SSL/TLS 记录协议（SSL/TLS Record Protocol），它建立在可靠的传输层协议（如TCP）之上，为上层协议提供数据封装、压缩、加密等基本功能。<br>　　2. SSL/TLS 握手协议（SSL/TLS Handshake Protocol），它建立在 SSL/TLS记录协议之上，用于在实际的数据传输开始前，通讯双方进行身份认证、协商加密算法、交换加密密钥等初始化协商功能。<br>　　从协议使用方式来看，又可以分成两种类型：<br>　　1. SSL/TLS 单向认证，就是用户到服务器之间只存在单方面的认证，即<strong>客户端会认证服务器端身份，而服务器端不会去对客户端身份进行验证</strong>。首先，客户端发起握手请求，服务器收到握手请求后，会选择适合双方的协议版本和加密方式。然后，再将协商的结果和服务器端的公钥一起发送给客户端。<em>客户端利用服务器端的公钥，对要发送的数据进行加密，并发送给服务器端</em>。服务器端收到后，会用本地私钥对收到的客户端加密数据进行解密。然后，通讯双方都会使用这些数据来产生双方之间通讯的加密密钥。接下来，双方就可以开始安全通讯过程了。<br>　　2.SSL/TLS 双向认证，就是双方都会互相认证，也就是两者之间将会交换证书。基本的过程和单向认证完全一样，只是在协商阶段多了几个步骤。在服务器端将协商的结果和服务器端的公钥一起发送给客户端后，会请求客户端的证书，客户端则会将证书发送给服务器端。然后，在客户端给服务器端发送加密数据后，客户端会将私钥生成的数字签名发送给服务器端。而服务器端则会用客户端证书中的公钥来验证数字签名的合法性。建立握手之后过程则和单向通讯完全保持一致。<br>　　SSL/TLS 协议建立通讯的基本流程如图所示<img src="/images/ssl-tls.png" alt="ssl-tls.png"></p>
<p>　　步骤 1. ClientHello – 客户端发送所支持的 SSL/TLS 最高协议版本号和所支持的加密算法集合及压缩方法集合等信息给服务器端。<br>　　步骤 2. ServerHello – 服务器端收到客户端信息后，选定双方都能够支持的 SSL/TLS 协议版本和加密方法及压缩方法，返回给客户端。<br>　　步骤 3（可选）. SendCertificate – 服务器端发送服务端证书给客户端。<br>　　步骤 4（可选）. RequestCertificate – 如果选择双向验证，服务器端向客户端请求客户端证书。<br>　　步骤 5. ServerHelloDone – 服务器端通知客户端初始协商结束。<br>　　步骤 6（可选）. ResponseCertificate – 如果选择双向验证，客户端向服务器端发送客户端证书。<br>　　步骤 7. ClientKeyExchange – 客户端使用服务器端的公钥，对客户端公钥和密钥种子进行加密，再发送给服务器端。<br>　　步骤 8（可选）. CertificateVerify – 如果选择双向验证，客户端用本地私钥生成数字签名，并发送给服务器端，让其通过收到的客户端公钥进行身份验证。<br>　　步骤 9. CreateSecretKey – 通讯双方基于密钥种子等信息生成通讯密钥。<br>　　步骤 10. ChangeCipherSpec – 客户端通知服务器端已将通讯方式切换到加密模式。<br>　　步骤 11. Finished – 客户端做好加密通讯的准备。<br>　　步骤 12. ChangeCipherSpec – 服务器端通知客户端已将通讯方式切换到加密模式。<br>　　步骤 13. Finished – 服务器做好加密通讯的准备。<br>　　步骤 14. Encrypted/DecryptedData – 双方使用客户端密钥，通过对称加密算法对通讯内容进行加密。<br>　　步骤 15. ClosedConnection – 通讯结束后，任何一方发出断开 SSL 连接的消息。<br>　　除了以上的基本流程，SSL/TLS 协议本身还有一些概念需要在此解释说明一下。<br>　　<strong>Key</strong>：Key 是一个比特（bit）字符串，用来加密解密数据的，就像是一把开锁的钥匙。<br>　　<strong>对称算法</strong>（symmetric cryptography）：就是需要双方使用一样的 key 来加密解密消息算法，常用密钥算法有 Data Encryption Standard（DES）、triple-strength DES（3DES）、Rivest Cipher 2 （RC2）和 Rivest Cipher 4（RC4）。因为对称算法效率相对较高，因此 SSL 会话中的敏感数据都用通过密钥算法加密。<br>　　<strong>非对称算法</strong>（asymmetric cryptography）：就是 key 的组成是公钥私钥对 （key-pair），公钥传递给对方私钥自己保留。公钥私钥算法是互逆的，一个用来加密，另一个可以解密。常用的算法有 Rivest Shamir Adleman（RSA）、Diffie-Hellman（DH）。非对称算法计算量大比较慢，因此仅适用于少量数据加密，如对密钥加密，而不适合大量数据的通讯加密。<br>　　<strong>公钥证书</strong>（public key certificate）：公钥证书类似数字护照，由受信机构颁发。受信组织的公钥证书就是 certificate authority（CA）。多证书可以连接成证书串，第一个是发送人，下一个是给其颁发证书实体，往上到根证书是世界范围受信组织，包括 VeriSign, Entrust, 和 GTE CyberTrust。公钥证书让非对称算法的公钥传递更安全，可以避免身份伪造，比如 C 创建了公钥私钥，对并冒充 A 将公钥传递给 B，这样 C 与 B 之间进行的通讯会让 B 误认是 A 与 B 之间通讯。<br>　　<strong>加密哈希功能</strong>（Cryptographic Hash Functions）： 加密哈希功能与 checksum 功能相似。不同之处在于，checksum 用来侦测意外的数据变化而前者用来侦测故意的数据篡改。数据被哈希后产生一小串比特字符串，微小的数据改变将导致哈希串的变化。发送加密数据时，SSL 会使用加密哈希功能来确保数据一致性，用来阻止第三方破坏通讯数据完整性。SSL 常用的哈希算法有 Message Digest 5（MD5）和 Secure Hash Algorithm（SHA）。<br>　　<strong>消息认证码</strong>（Message Authentication Code）： 消息认证码与加密哈希功能相似，除了它需要基于密钥。密钥信息与加密哈希功能产生的数据结合就是哈希消息认证码（HMAC）。如果 A 要确保给 B 发的消息不被 C 篡改，他要按如下步骤做 –A 首先要计算出一个 HMAC 值，将其添加到原始消息后面。用 A 与 B 之间通讯的密钥加密消息体，然后发送给 B。B 收到消息后用密钥解密，然后重新计算出一个 HMAC，来判断消息是否在传输中被篡改。SSL 用 HMAC 来保证数据传输的安全。<br>　　<strong>数字签名</strong>（Digital Signature）：一个消息的加密哈希被创建后，哈希值用发送者的私钥加密，加密的结果就是叫做数字签名。</p>
<h2 id="JSSE_uFF08Java_Secure_Socket_Extension_uFF09"><a href="#JSSE_uFF08Java_Secure_Socket_Extension_uFF09" class="headerlink" title="JSSE（Java Secure Socket Extension）"></a>JSSE（Java Secure Socket Extension）</h2><p>　　在 Java SDK 中有一个叫 JSSE（javax.net.ssl）包，这个包中提供了一些类来建立 SSL/TLS连接。通过这些类，开发者就可以忽略复杂的协议建立流程，较为简单地在网络上建成安全的通讯通道。JSSE 包中主要包括以下一些部分：<br>　　1. 安全套接字（secure socket）和安全服务器端套接字<br>　　2. 非阻塞式 SSL/TLS 数据处理引擎（SSLEngine）<br>　　3. 套接字创建工厂 , 用来产生 SSL 套接字和服务器端套接字<br>　　4. 套接字上下文 , 用来保存用于创建和数据引擎处理过程中的信息<br>　　5. 符合 X.509 规范密码匙和安全管理接口</p>
<p>　　下面将通过一个简单的例子来展示如何通过 JSSE，在客户端和服务器端建立一个 SSL/TLS 连接。设计两个类 SSLClient 和 SSLServer，分别来表示客户端和服务器端。客户端将会向服务器端发起连接请求，在通过服务器端验证建立 SSL 连接后，服务器端将会向客户端发送一串内容，客户端将会把收到的内容打印出来。<br>　　SSL Server类中需要用到ServerSocketFactory类来创建SSLServerSocket类实例，然后再通过SSLServerSocket来获取SSLSocket实例，这里考虑到面向对象中的面向接口编程的理念，所以代码中并没有出现SSLServerSocket和SSLSocket，而是用了他们的父类ServerSocket和Socket。在获取到ServerSocket和Socket实例以后，剩下的代码就和不使用加密方式一样了。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SSLServer</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 服务器端授权的用户名和密码</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String USER_NAME = <span class="string">"principal"</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String PASSWORD = <span class="string">"credential"</span>;</span><br><span class="line">    <span class="comment">// 服务器端保密内容</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String SECRET_CONTENT =</span><br><span class="line">            <span class="string">"This is confidential content from server X, for your eye!"</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> SSLServerSocket serverSocket = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">SSLServer</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        <span class="comment">// 通过套接字工厂，获取一个服务器端套接字</span></span><br><span class="line">        SSLServerSocketFactory socketFactory = (SSLServerSocketFactory)</span><br><span class="line">                SSLServerSocketFactory.getDefault();</span><br><span class="line">        serverSocket = (SSLServerSocket) socketFactory.createServerSocket(<span class="number">7070</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">runServer</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                System.out.println(<span class="string">"Waiting for connection..."</span>);</span><br><span class="line">                <span class="comment">// 服务器端套接字进入阻塞状态，等待来自客户端的连接请求</span></span><br><span class="line">                SSLSocket socket = (SSLSocket) serverSocket.accept();</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 获取服务器端套接字输入流</span></span><br><span class="line">                BufferedReader input = <span class="keyword">new</span> BufferedReader(</span><br><span class="line">                        <span class="keyword">new</span> InputStreamReader(socket.getInputStream()));</span><br><span class="line">                <span class="comment">// 从输入流中读取客户端用户名和密码</span></span><br><span class="line">                String userName = input.readLine();</span><br><span class="line">                String password = input.readLine();</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 获取服务器端套接字输出流</span></span><br><span class="line">                PrintWriter output = <span class="keyword">new</span> PrintWriter(</span><br><span class="line">                        <span class="keyword">new</span> OutputStreamWriter(socket.getOutputStream()));</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 对请求进行认证，如果通过则将保密内容发送给客户端</span></span><br><span class="line">                <span class="keyword">if</span> (userName.equals(USER_NAME) &amp;&amp; password.equals(PASSWORD)) &#123;</span><br><span class="line">                    output.println(<span class="string">"Welcome, "</span> + userName);</span><br><span class="line">                    output.println(SECRET_CONTENT);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    output.println(<span class="string">"Authentication failed, you have no access to server X..."</span>);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 关闭流资源和套接字资源</span></span><br><span class="line">                output.close();</span><br><span class="line">                input.close();</span><br><span class="line">                socket.close();</span><br><span class="line"></span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException ioException) &#123;</span><br><span class="line">                ioException.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String args[])</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        SSLServer server = <span class="keyword">new</span> SSLServer();</span><br><span class="line">        server.runServer();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　客户端则需要采用SSLSocketFactory生成一个Socket，示例如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SSLClient</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> SSLSocket socket = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">SSLClient</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="comment">// 通过套接字工厂，获取一个客户端套接字</span></span><br><span class="line">        SSLSocketFactory socketFactory = (SSLSocketFactory)</span><br><span class="line">                SSLSocketFactory.getDefault();</span><br><span class="line">        socket = (SSLSocket) socketFactory.createSocket(<span class="string">"localhost"</span>, <span class="number">7070</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">connect</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 获取客户端套接字输出流</span></span><br><span class="line">            PrintWriter output = <span class="keyword">new</span> PrintWriter(</span><br><span class="line">                    <span class="keyword">new</span> OutputStreamWriter(socket.getOutputStream()));</span><br><span class="line">            <span class="comment">// 将用户名和密码通过输出流发送到服务器端</span></span><br><span class="line">            String userName = <span class="string">"principal"</span>;</span><br><span class="line">            output.println(userName);</span><br><span class="line">            String password = <span class="string">"credential"</span>;</span><br><span class="line">            output.println(password);</span><br><span class="line">            output.flush();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 获取客户端套接字输入流</span></span><br><span class="line">            BufferedReader input = <span class="keyword">new</span> BufferedReader(</span><br><span class="line">                    <span class="keyword">new</span> InputStreamReader(socket.getInputStream()));</span><br><span class="line">            <span class="comment">// 从输入流中读取服务器端传送的数据内容，并打印出来</span></span><br><span class="line">            String response = input.readLine();</span><br><span class="line">            response += <span class="string">"\n "</span> + input.readLine();</span><br><span class="line">            System.out.println(response);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 关闭流资源和套接字资源</span></span><br><span class="line">            output.close();</span><br><span class="line">            input.close();</span><br><span class="line">            socket.close();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException ioException) &#123;</span><br><span class="line">            ioException.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            System.exit(<span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String args[])</span> <span class="keyword">throws</span> IOException </span>&#123;</span><br><span class="line">        <span class="keyword">new</span> SSLClient().connect();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>　　此时我们运行客户端和服务端代码，两端都会出现错误，从下面的关键错误可以看到： SSL 在握手阶段双方没有能够协商出加密方法等信息。这是因为默认情况下，java 虚拟机没有与 SSL 相关的配置，需要开发者自己按照文档进行一些配置。<br>　　1. 服务器端输出：javax.net.ssl.SSLHandshakeException: no cipher suites in common<br>　　2. 客户端输出：javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure<br>　　在 JDK 中提供了一个安全钥匙与证书的管理工具 Keytool。Keytool <strong>把钥匙，证书以及和与它们相关联的证书链储存到一个 keystore </strong>中，默任的实现中 keystore 的是一个文件，<strong>keystore本身有一个访问密码来保护存储在其中的内容</strong>。就本样例程序而言，只需要配置客户端和服务器端双方信任就可以了。可以按照如下几步来完成：</p>
<p>　　1. 进入本地的 java 安装位置的 bin 目录中 cd /java/bin<br>　　2. 创建一个客户端 keystore 文件，按提示输入信息即可<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">keytool -genkey -alias sslclient -keystore sslclientkeys</span><br></pre></td></tr></table></figure></p>
<p>　　3. 将客户端 keystore 文件导出成证书格式<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">keytool -export -alias sslclient -keystore sslclientkeys -file sslclient.cer</span><br></pre></td></tr></table></figure></p>
<p>　　4. 创建一个服务器端 keystore 文件<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">keytool -genkey -alias sslserver -keystore sslserverkeys</span><br></pre></td></tr></table></figure></p>
<p>　　5. 将服务器端 keystore 文件导出成证书格式<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">keytool -export -alias sslserver -keystore sslserverkeys -file sslserver.cer</span><br></pre></td></tr></table></figure></p>
<p>　　6. 将客户端证书导入到服务器端受信任的 keystore 中<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">keytool -import -alias sslclient -keystore sslservertrust -file sslclient.cer</span><br></pre></td></tr></table></figure></p>
<p>　　7. 将服务器端证书导入到客户端受信任的 keystore 中<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">keytool -import -alias sslserver -keystore sslclienttrust -file sslserver.cer</span><br></pre></td></tr></table></figure></p>
<p>　　8. 以上所有步骤都完成后，还可以通过命令来查看 keystore 文件基本信息<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">keytool -list -keystore sslclienttrust</span><br></pre></td></tr></table></figure></p>
<p>　　将前面创建的所有 keystore 文件从 java 的 bin 目录中剪切出来，移动到样例程序的执行目录中，通过运行程序时候的系统属性来指定这些文件，重新执行一遍样例程序。</p>
<p>  java -cp ./build/classes<br>      -Djavax.net.ssl.keyStore=sslserverkeys<br>      -Djavax.net.ssl.keyStorePassword=123456<br>      -Djavax.net.ssl.trustStore=sslservertrust<br>      -Djavax.net.ssl.trustStorePassword=123456<br>  example.ssl.codes.SSLServer </p>
<p>  java -cp ./build/classes<br>      -Djavax.net.ssl.keyStore=sslclientkeys<br>      -Djavax.net.ssl.keyStorePassword=123456<br>      -Djavax.net.ssl.trustStore=sslclienttrust<br>      -Djavax.net.ssl.trustStorePassword=123456<br>  example.ssl.codes.SSLClient</p>
<p>　　执行后，客户端与服务器端成功建立起 SSL 的连接，然后服务器端成功将字符串发送给客户端，客户端将其打印出来。客户端输出如下：<br>Welcome, principal<br> This is confidential content from server X, for your eye!<br>　　上面的示例完成了双向认证的过程，下面简单说一下单项认证的过程：<br>　　1. 终端进入%JAVA_HOME%\bin目录，运行如下：keytool.exe -genkey -alias mysocket -keyalg RSA -keystore mysocket.jks<br>　　2. 在提示输入项中，密码项自己给定，其它都不改直接回车，这里使用的密码是“mysocket”。生成的文件mysocket.jks也在目录%JAVA_HOME%\bin中<br>　　3. 将mysocket.jks拷贝到指定目录，运行Server<br>java -Djavax.net.ssl.keyStore=mysocket.jks -Djavax.net.ssl.keyStorePassword=mysocket demo5.MyServer<br>　　4. 运行Client<br>java -Djavax.net.ssl.trustStore=mysocket.jks  -Djavax.net.ssl.trustStorePassword=mysocket demo5.MyClient<br>　　5. 查看Server和Client的输出<br>　　这里我们没有导出服务端的keystore到证书文件，也没有将证书文件导入到客户端受信任的证书中。也没有导出客户端的keystore到证书文件，也没有将证书文件导入到服务端受信任的证书中。</p>
<h2 id="u4F18_u5316"><a href="#u4F18_u5316" class="headerlink" title="优化"></a>优化</h2><p>　　上面的代码在启动时，都需要-D参数，指定keystore和受信任证书的文件和密码。下面介绍如何在代码中实现该功能，特别是需要其他类型的证书支持时！实际的应用可以参考apple的apns服务。下面的实现主要代码为<a href="http://blog.csdn.net/a19881029/article/details/11742361" target="_blank" rel="external">http://blog.csdn.net/a19881029/article/details/11742361</a>中实现。只是在实验过程中发现存在数据掉包的问题，所以将其DataInputStream和DateOutputStream替换成了ObjectInputStream和ObjectOutputStream。请读者终端关注代码中的注释，加深理解</p>
<h3 id="u8BC1_u4E66"><a href="#u8BC1_u4E66" class="headerlink" title="证书"></a>证书</h3><p>　　首先我们生成需要的证书，如下：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#创建服务端keystore 私钥</span></span><br><span class="line">keytool -genkey -keystore .\certificate\server.jks -storepass <span class="number">1234</span>sp -keyalg RSA -keypass <span class="number">1234</span>kp</span><br><span class="line"><span class="comment">#创建客户端keystore 私钥</span></span><br><span class="line">keytool -genkey -keystore .\certificate\client.jks -storepass <span class="number">1234</span>sp -keyalg RSA -keypass <span class="number">1234</span>kp</span><br><span class="line"><span class="comment">#导出服务端证书 公钥</span></span><br><span class="line">keytool -export -keystore .\certificate\server.jks -storepass <span class="number">1234</span>sp -file .\certificate\server.cer</span><br><span class="line"><span class="comment">#导出客户端证书  公钥</span></span><br><span class="line">keytool -export -keystore .\certificate\client.jks -storepass <span class="number">1234</span>sp -file .\certificate\client.cer</span><br><span class="line"><span class="comment">#将服务端证书导入到客户端clientTrust，即服务端公钥告诉给了客户端</span></span><br><span class="line">keytool -import -keystore .\certificate\clientTrust.jks -storepass <span class="number">1234</span>sp -file .\certificate\server.cer</span><br><span class="line"><span class="comment">#将客户端证书导入到服务端serverTrust，即客户端公钥告诉给了服务端</span></span><br><span class="line">keytool -import -keystore .\certificate\serverTrust.jks -storepass <span class="number">1234</span>sp -file .\certificate\client.cer</span><br></pre></td></tr></table></figure></p>
<h3 id="u670D_u52A1_u7AEF_u8BA4_u8BC1"><a href="#u670D_u52A1_u7AEF_u8BA4_u8BC1" class="headerlink" title="服务端认证"></a>服务端认证</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Auth</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> SSLContext sslContext;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> SSLContext <span class="title">getSSLContext</span><span class="params">()</span> <span class="keyword">throws</span> Exception</span>&#123;</span><br><span class="line">        Properties p = Configuration.getConfig();</span><br><span class="line">        String protocol = p.getProperty(<span class="string">"protocol"</span>);</span><br><span class="line">        String serverCer = p.getProperty(<span class="string">"serverCer"</span>);</span><br><span class="line">        String serverCerPwd = p.getProperty(<span class="string">"serverCerPwd"</span>);</span><br><span class="line">        String serverKeyPwd = p.getProperty(<span class="string">"serverKeyPwd"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//加载服务端keystore</span></span><br><span class="line">        KeyStore keyStore = KeyStore.getInstance(<span class="string">"JKS"</span>);</span><br><span class="line">        keyStore.load(Auth.class.getResourceAsStream(serverCer),</span><br><span class="line">                serverCerPwd.toCharArray());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//获取keystore中所有key链</span></span><br><span class="line">        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(<span class="string">"SunX509"</span>);</span><br><span class="line">        keyManagerFactory.init(keyStore, serverKeyPwd.toCharArray());</span><br><span class="line">        KeyManager[] kms = keyManagerFactory.getKeyManagers();</span><br><span class="line"></span><br><span class="line">        TrustManager[] tms = <span class="keyword">null</span>;</span><br><span class="line">        <span class="comment">// 如果采用双向认证（服务端也需要认证客户端），</span></span><br><span class="line">        <span class="comment">// 服务端则需要加载服务端受信任证书（客户端公钥已写入服务端务端受信任证书）</span></span><br><span class="line">        <span class="keyword">if</span>(Configuration.getConfig().getProperty(<span class="string">"authority"</span>).equals(<span class="string">"2"</span>))&#123;</span><br><span class="line">            String serverTrustCer = p.getProperty(<span class="string">"serverTrustCer"</span>);</span><br><span class="line">            String serverTrustCerPwd = p.getProperty(<span class="string">"serverTrustCerPwd"</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//加载受信任证书的Key Store</span></span><br><span class="line">            keyStore = KeyStore.getInstance(<span class="string">"JKS"</span>);</span><br><span class="line">            keyStore.load(Auth.class.getResourceAsStream(serverTrustCer),</span><br><span class="line">                    serverTrustCerPwd.toCharArray());</span><br><span class="line">            <span class="comment">//从keystrore中获取受信任证书链</span></span><br><span class="line">            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(<span class="string">"SunX509"</span>);</span><br><span class="line">            trustManagerFactory.init(keyStore);</span><br><span class="line">            tms = trustManagerFactory.getTrustManagers();</span><br><span class="line">        &#125;</span><br><span class="line">        sslContext = SSLContext.getInstance(protocol);</span><br><span class="line">        <span class="comment">// 第一个参数为服务端所有私钥</span></span><br><span class="line">        <span class="comment">// 第二个参数为客户端所有公钥（双向认证），为空则表示单项认证（客户端验证服务端）</span></span><br><span class="line">        sslContext.init(kms, tms, <span class="keyword">null</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> sslContext;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="u670D_u52A1_u7AEF_u4E3B_u7A0B_u5E8F"><a href="#u670D_u52A1_u7AEF_u4E3B_u7A0B_u5E8F" class="headerlink" title="服务端主程序"></a>服务端主程序</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Server</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> SSLContext sslContext;</span><br><span class="line">    <span class="keyword">private</span> SSLServerSocketFactory sslServerSocketFactory;</span><br><span class="line">    <span class="keyword">private</span> SSLServerSocket sslServerSocket;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Executor executor;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Server</span><span class="params">()</span> <span class="keyword">throws</span> Exception</span>&#123;</span><br><span class="line">        Properties p = Configuration.getConfig();</span><br><span class="line">        Integer serverListenPort = Integer.valueOf(p.getProperty(<span class="string">"serverListenPort"</span>));</span><br><span class="line">        Integer serverThreadPoolSize = Integer.valueOf(p.getProperty(<span class="string">"serverThreadPoolSize"</span>));</span><br><span class="line">        Integer serverRequestQueueSize = Integer.valueOf(p.getProperty(<span class="string">"serverRequestQueueSize"</span>));</span><br><span class="line">        Integer authority = Integer.valueOf(p.getProperty(<span class="string">"authority"</span>));</span><br><span class="line"></span><br><span class="line">        executor = Executors.newFixedThreadPool(serverThreadPoolSize);</span><br><span class="line"></span><br><span class="line">        sslContext = Auth.getSSLContext();</span><br><span class="line">        sslServerSocketFactory = sslContext.getServerSocketFactory();</span><br><span class="line">        <span class="comment">//只是创建一个TCP连接，SSL handshake还没开始</span></span><br><span class="line">        <span class="comment">//客户端或服务端第一次试图获取socket输入流或输出流时，</span></span><br><span class="line">        <span class="comment">//SSL handshake才会开始</span></span><br><span class="line">        <span class="comment">//加密套件（ciphersuits）包括一组加密参数，这些参数指定了加密算法、密钥长度等加密等信息。</span></span><br><span class="line">        sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket();</span><br><span class="line">        String[] pwdsuits = sslServerSocket.getSupportedCipherSuites();</span><br><span class="line">        sslServerSocket.setEnabledCipherSuites(pwdsuits);</span><br><span class="line">        <span class="comment">//默认是client mode，必须在握手开始之前调用</span></span><br><span class="line">        sslServerSocket.setUseClientMode(<span class="keyword">false</span>);</span><br><span class="line">        <span class="keyword">if</span>(authority.intValue() == <span class="number">2</span>)&#123;</span><br><span class="line">            <span class="comment">//只有设置为server mode，该配置才生效</span></span><br><span class="line">            <span class="comment">//如果客户端不提供其证书，通信将会结束</span></span><br><span class="line">            sslServerSocket.setNeedClientAuth(<span class="keyword">true</span>);</span><br><span class="line">        &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">            <span class="comment">//只有设置为server mode，该配置才生效</span></span><br><span class="line">            <span class="comment">//即使客户端不提供其证书，通信也将继续</span></span><br><span class="line">            sslServerSocket.setWantClientAuth(<span class="keyword">true</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        sslServerSocket.setReuseAddress(<span class="keyword">true</span>);</span><br><span class="line">        sslServerSocket.setReceiveBufferSize(<span class="number">128</span>*<span class="number">1024</span>);</span><br><span class="line">        <span class="comment">//空方法</span></span><br><span class="line">        sslServerSocket.setPerformancePreferences(<span class="number">3</span>, <span class="number">2</span>, <span class="number">1</span>);</span><br><span class="line">        <span class="comment">//第二个参数为backlog</span></span><br><span class="line">        sslServerSocket.bind(<span class="keyword">new</span> InetSocketAddress(serverListenPort), serverRequestQueueSize);</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">"Server start up!"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">service</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">while</span>(<span class="keyword">true</span>)&#123;</span><br><span class="line">            SSLSocket socket = <span class="keyword">null</span>;</span><br><span class="line">            <span class="keyword">try</span>&#123;</span><br><span class="line">                System.out.println(<span class="string">"Wait for client request!"</span>);</span><br><span class="line">                socket = (SSLSocket)sslServerSocket.accept();</span><br><span class="line">                System.out.println(<span class="string">"Get client request!"</span>);</span><br><span class="line">                socket.setTcpNoDelay(<span class="keyword">true</span>);</span><br><span class="line">                <span class="comment">//新的连接建立，处理则放到线程池中，避免阻塞</span></span><br><span class="line">                Runnable job = <span class="keyword">new</span> Job(socket);</span><br><span class="line">                executor.execute(job);</span><br><span class="line">            &#125;<span class="keyword">catch</span>(Exception e)&#123;</span><br><span class="line">                System.out.println(<span class="string">"server run exception"</span>);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    socket.close();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (IOException e1) &#123;</span><br><span class="line">                    e1.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Server server;</span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            server = <span class="keyword">new</span> Server();</span><br><span class="line">            server.service();</span><br><span class="line">        &#125;<span class="keyword">catch</span>(Exception e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            System.out.println(<span class="string">"server socket establish error!"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="u6570_u636E_u5904_u7406"><a href="#u6570_u636E_u5904_u7406" class="headerlink" title="数据处理"></a>数据处理</h3><p>　　在一个新的线程中处理客户端发来的数据<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Job</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Socket socket;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Job</span><span class="params">(Socket socket)</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.socket = socket;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            ObjectInputStream ois = SocketIO.getObjectInput(socket);</span><br><span class="line">            String user = ois.readUTF();</span><br><span class="line">            <span class="keyword">int</span> pwd = ois.readInt();</span><br><span class="line">            System.out.println(<span class="string">"request user:"</span> + user);</span><br><span class="line">            System.out.println(<span class="string">"request pwd:"</span> + pwd);</span><br><span class="line"></span><br><span class="line">            String result = <span class="keyword">null</span>;</span><br><span class="line">            <span class="keyword">if</span>(<span class="keyword">null</span> != user &amp;&amp; !user.equals(<span class="string">""</span>) &amp;&amp; user.equals(<span class="string">"name"</span>) &amp;&amp; pwd == <span class="number">123</span>)&#123;</span><br><span class="line">                result = <span class="string">"login success"</span>;</span><br><span class="line">            &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">                result = <span class="string">"login failed"</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            ObjectOutputStream oos = SocketIO.getObjectOutput(socket);</span><br><span class="line">            oos.writeUTF(result);</span><br><span class="line">            oos.flush();</span><br><span class="line">            System.out.println(<span class="string">"response info:"</span> + result);</span><br><span class="line">        &#125;<span class="keyword">catch</span>(Exception e)&#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            System.out.println(<span class="string">"business thread run exception"</span>);</span><br><span class="line">        &#125;<span class="keyword">finally</span>&#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                socket.close();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">                System.out.println(<span class="string">"server socket close error"</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h3 id="u5BA2_u6237_u7AEF_u8BA4_u8BC1"><a href="#u5BA2_u6237_u7AEF_u8BA4_u8BC1" class="headerlink" title="客户端认证"></a>客户端认证</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Auth</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> SSLContext sslContext;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> SSLContext <span class="title">getSSLContext</span><span class="params">()</span> <span class="keyword">throws</span> Exception</span>&#123;</span><br><span class="line">        Properties p = Configuration.getConfig();</span><br><span class="line">        String protocol = p.getProperty(<span class="string">"protocol"</span>);</span><br><span class="line">        String clientTrustCerFile = p.getProperty(<span class="string">"clientTrustCer"</span>);</span><br><span class="line">        String clientTrustCerPwd = p.getProperty(<span class="string">"clientTrustCerPwd"</span>);</span><br><span class="line"></span><br><span class="line">        KeyStore keyStore = KeyStore.getInstance(<span class="string">"JKS"</span>);</span><br><span class="line">        <span class="comment">// 不管是单向认证，还是双向认证，客户端都需要验证服务端</span></span><br><span class="line">        <span class="comment">// 此时客户端则需要加载客户端受信任证书（信任哪些服务器）</span></span><br><span class="line">        <span class="comment">// 该证书中包含有服务端的公钥（可以这么理解）</span></span><br><span class="line">        keyStore.load(Auth.class.getResourceAsStream(clientTrustCerFile),</span><br><span class="line">                clientTrustCerPwd.toCharArray());</span><br><span class="line"></span><br><span class="line">        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(<span class="string">"SunX509"</span>);</span><br><span class="line">        trustManagerFactory.init(keyStore);</span><br><span class="line">        TrustManager[] tms = trustManagerFactory.getTrustManagers();</span><br><span class="line"></span><br><span class="line">        KeyManager[] kms = <span class="keyword">null</span>;</span><br><span class="line">        <span class="comment">// 双线认证，则需要服务端验证客户端的证书</span></span><br><span class="line">        <span class="comment">// 服务端读取器其受信任证书（信任哪些客户端，可以理解为证书中含有客户端公钥）</span></span><br><span class="line">        <span class="comment">// 服务端加载客户端公钥，客户端则需要加载客户端私钥</span></span><br><span class="line">        <span class="keyword">if</span>(Configuration.getConfig().getProperty(<span class="string">"authority"</span>).equals(<span class="string">"2"</span>))&#123;</span><br><span class="line">            String clientCerFile = p.getProperty(<span class="string">"clientCer"</span>);</span><br><span class="line">            String clientCerPwd = p.getProperty(<span class="string">"clientCerPwd"</span>);</span><br><span class="line">            String clientKeyPwd = p.getProperty(<span class="string">"clientKeyPwd"</span>);</span><br><span class="line"></span><br><span class="line">            keyStore = KeyStore.getInstance(<span class="string">"JKS"</span>);</span><br><span class="line">            keyStore.load(Auth.class.getResourceAsStream(clientCerFile),</span><br><span class="line">                    clientCerPwd.toCharArray());</span><br><span class="line"></span><br><span class="line">            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(<span class="string">"SunX509"</span>);</span><br><span class="line">            keyManagerFactory.init(keyStore, clientKeyPwd.toCharArray());</span><br><span class="line">            kms = keyManagerFactory.getKeyManagers();</span><br><span class="line">        &#125;</span><br><span class="line">        sslContext = SSLContext.getInstance(protocol);</span><br><span class="line">        sslContext.init(kms, tms, <span class="keyword">null</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> sslContext;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="u5BA2_u6237_u7AEF_u4E3B_u7A0B_u5E8F"><a href="#u5BA2_u6237_u7AEF_u4E3B_u7A0B_u5E8F" class="headerlink" title="客户端主程序"></a>客户端主程序</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Client</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> SSLContext sslContext;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> port = <span class="number">10000</span>;</span><br><span class="line">    <span class="keyword">private</span> String host = <span class="string">"127.0.0.1"</span>;</span><br><span class="line">    <span class="keyword">private</span> SSLSocket socket;</span><br><span class="line">    <span class="keyword">private</span> Properties p;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Client</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            p = Configuration.getConfig();</span><br><span class="line"></span><br><span class="line">            sslContext = Auth.getSSLContext();</span><br><span class="line">            SSLSocketFactory factory = sslContext.getSocketFactory();</span><br><span class="line">            socket = (SSLSocket) factory.createSocket();</span><br><span class="line">            String[] pwdsuits = socket.getSupportedCipherSuites();</span><br><span class="line">            <span class="comment">//socket可以使用所有支持的加密套件</span></span><br><span class="line">            <span class="comment">//加密套件（ciphersuits）包括一组加密参数，这些参数指定了加密算法、密钥长度等加密等信息。</span></span><br><span class="line">            socket.setEnabledCipherSuites(pwdsuits);</span><br><span class="line">            <span class="comment">//默认就是true</span></span><br><span class="line">            socket.setUseClientMode(<span class="keyword">true</span>);</span><br><span class="line">            socket.setTcpNoDelay(<span class="keyword">true</span>);</span><br><span class="line">            SocketAddress address = <span class="keyword">new</span> InetSocketAddress(host, port);</span><br><span class="line">            socket.connect(address, <span class="number">0</span>);</span><br><span class="line">            <span class="comment">//握手完成监听器</span></span><br><span class="line">            MyHandshakeCompletedListener listener = <span class="keyword">new</span> MyHandshakeCompletedListener();</span><br><span class="line">            socket.addHandshakeCompletedListener(listener);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            System.out.println(<span class="string">"socket establish failed!"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">request</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            String user = <span class="string">"name"</span>;</span><br><span class="line">            <span class="keyword">int</span> pwd = <span class="number">123</span>;</span><br><span class="line">            ObjectOutputStream oos = SocketIO.getObjectOutput(socket);</span><br><span class="line">            oos.writeUTF(user);</span><br><span class="line">            oos.writeInt(pwd);</span><br><span class="line">            oos.flush();</span><br><span class="line"></span><br><span class="line">            ObjectInputStream ois = SocketIO.getObjectInput(socket);</span><br><span class="line">            String receive = ois.readUTF();</span><br><span class="line">            System.out.println(<span class="string">"request result:"</span> + receive);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            System.out.println(<span class="string">"request error"</span>);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                socket.close();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Client client = <span class="keyword">new</span> Client();</span><br><span class="line">        client.request();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="u5DE5_u5177_u7C7B"><a href="#u5DE5_u5177_u7C7B" class="headerlink" title="工具类"></a>工具类</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Configuration</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Properties config;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Properties <span class="title">getConfig</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            <span class="keyword">if</span>(<span class="keyword">null</span> == config)&#123;</span><br><span class="line">                InputStream input = Configuration.class.getResourceAsStream(<span class="string">"/conf/conf.properties"</span>);</span><br><span class="line">                config = <span class="keyword">new</span> Properties();</span><br><span class="line">                config.load(input);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;<span class="keyword">catch</span>(Exception e)&#123;</span><br><span class="line">            config = <span class="keyword">new</span> Properties();</span><br><span class="line">            config.setProperty(<span class="string">"protocol"</span>, <span class="string">"TLSV1"</span>);</span><br><span class="line">            config.setProperty(<span class="string">"serverCer"</span>, <span class="string">"/certificate/server.jks"</span>);</span><br><span class="line">            config.setProperty(<span class="string">"serverCerPwd"</span>, <span class="string">"1234sp"</span>);</span><br><span class="line">            config.setProperty(<span class="string">"serverKeyPwd"</span>, <span class="string">"1234kp"</span>);</span><br><span class="line">            config.setProperty(<span class="string">"serverTrustCer"</span>, <span class="string">"/certificate/serverTrust.jks"</span>);</span><br><span class="line">            config.setProperty(<span class="string">"serverTrustCerPwd"</span>, <span class="string">"1234sp"</span>);</span><br><span class="line">            config.setProperty(<span class="string">"clientCer"</span>, <span class="string">"/certificate/client.jks"</span>);</span><br><span class="line">            config.setProperty(<span class="string">"clientCerPwd"</span>, <span class="string">"1234sp"</span>);</span><br><span class="line">            config.setProperty(<span class="string">"clientKeyPwd"</span>, <span class="string">"1234kp"</span>);</span><br><span class="line">            config.setProperty(<span class="string">"clientTrustCer"</span>, <span class="string">"/certificate/clientTrust.jks"</span>);</span><br><span class="line">            config.setProperty(<span class="string">"clientTrustCerPwd"</span>, <span class="string">"1234sp"</span>);</span><br><span class="line">            config.setProperty(<span class="string">"serverListenPort"</span>, <span class="string">"10000"</span>);</span><br><span class="line">            config.setProperty(<span class="string">"serverThreadPoolSize"</span>, <span class="string">"5"</span>);</span><br><span class="line">            config.setProperty(<span class="string">"serverRequestQueueSize"</span>, <span class="string">"10"</span>);</span><br><span class="line">            config.setProperty(<span class="string">"socketStreamEncoding"</span>, <span class="string">"UTF-8"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> config;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyHandshakeCompletedListener</span> <span class="keyword">implements</span> <span class="title">HandshakeCompletedListener</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handshakeCompleted</span><span class="params">(HandshakeCompletedEvent arg0)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"Handshake finished successfully"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SocketIO</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ObjectInputStream <span class="title">getObjectInput</span><span class="params">(Socket socket)</span> <span class="keyword">throws</span> IOException</span>&#123;</span><br><span class="line">        ObjectInputStream ois = <span class="keyword">new</span> ObjectInputStream(socket.getInputStream());</span><br><span class="line">        <span class="keyword">return</span> ois;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ObjectOutputStream <span class="title">getObjectOutput</span><span class="params">(Socket socket)</span> <span class="keyword">throws</span> IOException</span>&#123;</span><br><span class="line">        ObjectOutputStream oos = <span class="keyword">new</span> ObjectOutputStream(socket.getOutputStream());</span><br><span class="line">        <span class="keyword">return</span> oos;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="u603B_u7ED3"><a href="#u603B_u7ED3" class="headerlink" title="总结"></a>总结</h2><p>　　到这里我们也就完成了SSL通信的讲解，事实上，这里讲解的主要是socket，如何实现NIO的呢，请读者思考，建议大家参考一下netty中的实现。简单点说SSL通信，其实无非下面几点：<br>　　1. 服务端加载其私钥，客户端加载服务端公钥（服务端证书导出到客户端受信任的证书中，则形成公钥）<br>　　3. 如果为双向通信，则还需要客户端加载其私钥，服务端加载客户端公钥。<br>　　再次强调一下，请读者重点关注程序中的注释，方便理解。完整源码请下载：<a href="/code/ssl-socket.zip">ssl-socket.zip</a></p>
<h2 id="u53C2_u8003_u8D44_u6599"><a href="#u53C2_u8003_u8D44_u6599" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="http://www.ibm.com/developerworks/cn/java/j-lo-ssltls/" target="_blank" rel="external">http://www.ibm.com/developerworks/cn/java/j-lo-ssltls/</a><br><a href="http://blog.csdn.net/a19881029/article/details/11742361" target="_blank" rel="external">http://blog.csdn.net/a19881029/article/details/11742361</a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://yoursite.com/2015/12/23/nio/2.SSL Socket/" data-id="cijzzpv7000581cfsx7yaoj7s" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/java/">java</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/socket/">socket</a></li></ul>

    </footer>
  </div>
  
    
<nav id="article-nav">
  
    <a href="/2015/12/23/nio/3.NIO入门/" id="article-nav-newer" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Newer</strong>
      <div class="article-nav-title">
        
          NIO入门
        
      </div>
    </a>
  
  
    <a href="/2015/12/23/nio/1.Socket入门/" id="article-nav-older" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Older</strong>
      <div class="article-nav-title">Socket入门</div>
    </a>
  
</nav>

  
</article>

</section>
        
          <aside id="sidebar">
  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Categories</h3>
    <div class="widget">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/cache/">cache</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/database/">database</a><span class="category-list-count">13</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/docker/">docker</a><span class="category-list-count">6</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/gis/">gis</a><span class="category-list-count">4</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/git/">git</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/go/">go</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/gradle/">gradle</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/groovy/">groovy</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/hadoop/">hadoop</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/java/">java</a><span class="category-list-count">18</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/linux/">linux</a><span class="category-list-count">4</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/log/">log</a><span class="category-list-count">11</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/mongodb/">mongodb</a><span class="category-list-count">14</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/mq/">mq</a><span class="category-list-count">6</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/mybatis/">mybatis</a><span class="category-list-count">9</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/netty/">netty</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/nginx/">nginx</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/nio/">nio</a><span class="category-list-count">19</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/nodejs/">nodejs</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/redis/">redis</a><span class="category-list-count">6</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/rpc/">rpc</a><span class="category-list-count">14</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/spring/">spring</a><span class="category-list-count">5</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/tomcat/">tomcat</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/zookeeper/">zookeeper</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/杂项/">杂项</a><span class="category-list-count">2</span></li></ul>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tags</h3>
    <div class="widget">
      <ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/aop/">aop</a><span class="tag-list-count">8</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/aspectj/">aspectj</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/cache/">cache</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/classloader/">classloader</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/common-loggings/">common-loggings</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/commonjs/">commonjs</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/database/">database</a><span class="tag-list-count">10</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/docker/">docker</a><span class="tag-list-count">6</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/dubbo/">dubbo</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/fastdfs/">fastdfs</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/gclib/">gclib</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/geohash/">geohash</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/gis/">gis</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/git/">git</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/go/">go</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/gradle/">gradle</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/groovy/">groovy</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/hadoop/">hadoop</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/hessisan/">hessisan</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/java/">java</a><span class="tag-list-count">48</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/javassist/">javassist</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/kafka/">kafka</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/linux/">linux</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/log/">log</a><span class="tag-list-count">11</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/log4j/">log4j</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/logback/">logback</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/map/">map</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/memecached/">memecached</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mina/">mina</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mongodb/">mongodb</a><span class="tag-list-count">14</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mq/">mq</a><span class="tag-list-count">6</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mqtt/">mqtt</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mybatis/">mybatis</a><span class="tag-list-count">9</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/netty/">netty</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nginx/">nginx</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nio/">nio</a><span class="tag-list-count">5</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nodejs/">nodejs</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/postgresql/">postgresql</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/protobuf/">protobuf</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/proxy/">proxy</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/push/">push</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/rabbitmq/">rabbitmq</a><span class="tag-list-count">3</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/redis/">redis</a><span class="tag-list-count">6</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/rpc/">rpc</a><span class="tag-list-count">14</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/sitemesh/">sitemesh</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/sld/">sld</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/slf4j/">slf4j</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/socket/">socket</a><span class="tag-list-count">7</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spatialite-for-android/">spatialite for android</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spi/">spi</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spring/">spring</a><span class="tag-list-count">13</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/tomcat/">tomcat</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/transaction/">transaction</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/web/">web</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/xSocket/">xSocket</a><span class="tag-list-count">7</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/zbus/">zbus</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/zookeeper/">zookeeper</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/数据结构/">数据结构</a><span class="tag-list-count">2</span></li></ul>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tag Cloud</h3>
    <div class="widget tagcloud">
      <a href="/tags/aop/" style="font-size: 15.38px;">aop</a> <a href="/tags/aspectj/" style="font-size: 11.54px;">aspectj</a> <a href="/tags/cache/" style="font-size: 11.54px;">cache</a> <a href="/tags/classloader/" style="font-size: 10px;">classloader</a> <a href="/tags/common-loggings/" style="font-size: 10px;">common-loggings</a> <a href="/tags/commonjs/" style="font-size: 12.31px;">commonjs</a> <a href="/tags/database/" style="font-size: 16.92px;">database</a> <a href="/tags/docker/" style="font-size: 13.85px;">docker</a> <a href="/tags/dubbo/" style="font-size: 10px;">dubbo</a> <a href="/tags/fastdfs/" style="font-size: 10px;">fastdfs</a> <a href="/tags/gclib/" style="font-size: 10.77px;">gclib</a> <a href="/tags/geohash/" style="font-size: 10px;">geohash</a> <a href="/tags/gis/" style="font-size: 10px;">gis</a> <a href="/tags/git/" style="font-size: 10.77px;">git</a> <a href="/tags/go/" style="font-size: 10px;">go</a> <a href="/tags/gradle/" style="font-size: 10px;">gradle</a> <a href="/tags/groovy/" style="font-size: 10px;">groovy</a> <a href="/tags/hadoop/" style="font-size: 10px;">hadoop</a> <a href="/tags/hessisan/" style="font-size: 11.54px;">hessisan</a> <a href="/tags/java/" style="font-size: 20px;">java</a> <a href="/tags/javassist/" style="font-size: 10px;">javassist</a> <a href="/tags/kafka/" style="font-size: 11.54px;">kafka</a> <a href="/tags/linux/" style="font-size: 12.31px;">linux</a> <a href="/tags/log/" style="font-size: 17.69px;">log</a> <a href="/tags/log4j/" style="font-size: 12.31px;">log4j</a> <a href="/tags/logback/" style="font-size: 13.08px;">logback</a> <a href="/tags/map/" style="font-size: 10px;">map</a> <a href="/tags/memecached/" style="font-size: 10.77px;">memecached</a> <a href="/tags/mina/" style="font-size: 13.08px;">mina</a> <a href="/tags/mongodb/" style="font-size: 19.23px;">mongodb</a> <a href="/tags/mq/" style="font-size: 13.85px;">mq</a> <a href="/tags/mqtt/" style="font-size: 10.77px;">mqtt</a> <a href="/tags/mybatis/" style="font-size: 16.15px;">mybatis</a> <a href="/tags/netty/" style="font-size: 10px;">netty</a> <a href="/tags/nginx/" style="font-size: 11.54px;">nginx</a> <a href="/tags/nio/" style="font-size: 13.08px;">nio</a> <a href="/tags/nodejs/" style="font-size: 10px;">nodejs</a> <a href="/tags/postgresql/" style="font-size: 10.77px;">postgresql</a> <a href="/tags/protobuf/" style="font-size: 10px;">protobuf</a> <a href="/tags/proxy/" style="font-size: 10px;">proxy</a> <a href="/tags/push/" style="font-size: 10px;">push</a> <a href="/tags/rabbitmq/" style="font-size: 11.54px;">rabbitmq</a> <a href="/tags/redis/" style="font-size: 13.85px;">redis</a> <a href="/tags/rpc/" style="font-size: 19.23px;">rpc</a> <a href="/tags/sitemesh/" style="font-size: 10px;">sitemesh</a> <a href="/tags/sld/" style="font-size: 10px;">sld</a> <a href="/tags/slf4j/" style="font-size: 10px;">slf4j</a> <a href="/tags/socket/" style="font-size: 14.62px;">socket</a> <a href="/tags/spatialite-for-android/" style="font-size: 10px;">spatialite for android</a> <a href="/tags/spi/" style="font-size: 10px;">spi</a> <a href="/tags/spring/" style="font-size: 18.46px;">spring</a> <a href="/tags/tomcat/" style="font-size: 10.77px;">tomcat</a> <a href="/tags/transaction/" style="font-size: 10px;">transaction</a> <a href="/tags/web/" style="font-size: 10px;">web</a> <a href="/tags/xSocket/" style="font-size: 14.62px;">xSocket</a> <a href="/tags/zbus/" style="font-size: 10px;">zbus</a> <a href="/tags/zookeeper/" style="font-size: 10.77px;">zookeeper</a> <a href="/tags/数据结构/" style="font-size: 10.77px;">数据结构</a>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Archives</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2016/01/">January 2016</a><span class="archive-list-count">67</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2015/12/">December 2015</a><span class="archive-list-count">81</span></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2015/11/">November 2015</a><span class="archive-list-count">2</span></li></ul>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Recents</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/2016/01/30/资料/">(no title)</a>
          </li>
        
          <li>
            <a href="/2016/01/30/java/13.Classloader系列一/">ClassLoader系列一</a>
          </li>
        
          <li>
            <a href="/2016/01/29/hadoop/1.hadoop系列之源码编译/">hadoop系列一之源码编译</a>
          </li>
        
          <li>
            <a href="/2016/01/29/go/1.GO系列之入门/">Go系列一之入门</a>
          </li>
        
          <li>
            <a href="/2016/01/28/tomcat/2.tomcat性能优化/">tomcat性能优化</a>
          </li>
        
      </ul>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      &copy; 2016 John Doe<br>
      Powered by <a href="http://hexo.io/" target="_blank">Hexo</a>
    </div>
  </div>
</footer>
    </div>
    <nav id="mobile-nav">
  
    <a href="/" class="mobile-nav-link">Home</a>
  
    <a href="/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    


<script src="http://libs.baidu.com/jquery/2.0.3/jquery.min.js"></script>


  <link rel="stylesheet" href="/fancybox/jquery.fancybox.css" type="text/css">
  <script src="/fancybox/jquery.fancybox.pack.js" type="text/javascript"></script>


<script src="/js/script.js" type="text/javascript"></script>

  </div>
</body>
</html>